package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.service.UserPaidTrackService;
import com.atguigu.tingshu.user.strategy.PaidRecordStrategy;
import com.atguigu.tingshu.user.strategy.factory.StrategyFactory;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private UserPaidTrackService userPaidTrackService;

    @Autowired
    private AlbumFeignClient albumFeignClient;


    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    /**
     * 微信登录
     *
     * @param code
     * @return
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.对接微信登录接口获取微信账户唯一标识；openId
            WxMaUserService userService = wxMaService.getUserService();
            WxMaJscode2SessionResult sessionResult = userService.getSessionInfo(code);
            String wxOpenid = sessionResult.getOpenid();

            //2.判断当前微信账户是否已经登录过 根据微信唯一标识查询用户记录
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getWxOpenId, wxOpenid);
            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

            //3.如果用户是首次登录，初始化用户记录关联微信账户，基于MQ初始账户（余额）记录
            if (userInfo == null) {
                //3.1 构建用户信息对象封装基本属性，关联微信账户OpenID
                userInfo = new UserInfo();
                userInfo.setWxOpenId(wxOpenid);
                userInfo.setNickname("听友" + IdUtil.nanoId());
                userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-03-10/110d2ee9-f5fb-4a76-96fb-cec750dc4fb3.jpg");
                //3.2 保存用户信息
                userInfoMapper.insert(userInfo);
                //3.3 TODO 基于MQ通知账户服务-初始账户（余额）记录
                //3.3.1 构建消息内容 赠送体验金
                Map<String, Object> map = new HashMap<>();
                map.put("userId", userInfo.getId());
                map.put("amount", new BigDecimal("100"));
                String orderNo = "ZS" + DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
                map.put("orderNo", orderNo);
                map.put("title", "首次注册赠送");
                //3.3.2 发送消息
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, map);

            }
            //4.基于用户信息生成token，将用户基本信息作为Value存入Redis
            //4.1 生成随机UUID作为令牌
            String token = IdUtil.randomUUID();
            //4.2 构建用户登录key ---> user:login:token
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //4.3 将用户基本信息UserInfoVo存入Redis 存储7天有效
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
            //5.将令牌封装到Map中
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (WxErrorException e) {
            log.error("[用户服务]微信登录异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询指定用户基本信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = baseMapper.selectById(userId);
        return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    }

    /**
     * 更新用户信息
     *
     * @param userInfoVo
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo) {
        //1.TODO 删除缓存
        Long userId = AuthContextHolder.getUserId();
        UserInfo userInfo = baseMapper.selectById(userId);
        //注意：只允许修改部分信息
        userInfo.setNickname(userInfoVo.getNickname());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        baseMapper.updateById(userInfo);
        //3.TODO 睡眠一段时间 确保并发读线程执行完毕，将"脏"数据写回Redis
        //4.TODO 再删除一次缓存
    }

    /**
     * 根据用户ID + 专辑ID + 音轨ID列表查询用户购买情况
     *
     * @param userId
     * @param albumId
     * @param needCheckBuyStateTrackIds
     * @return
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckBuyStateTrackIds) {
        Map<Long, Integer> map = new HashMap<>();
        //1.尝试根据用户ID+专辑ID查询已购专辑 如果存在专辑购买记录，则将提交声音购买情况设置为1，返回即可
        Long count = userPaidAlbumMapper.selectCount(
                new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getUserId, userId)
                        .eq(UserPaidAlbum::getAlbumId, albumId)
        );
        if (count > 0) {
            for (Long needCheckBuyStateTrackId : needCheckBuyStateTrackIds) {
                map.put(needCheckBuyStateTrackId, 1);
            }
            return map;
        }
        //2.如果用户没有购买专辑，则尝试根据用户ID+专辑ID，查询已购声音列表
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper
                .selectList(
                        new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userId)
                                .eq(UserPaidTrack::getAlbumId, albumId)
                );
        //2.1 如果已购声音列表不存在，则将提交声音购买情况设置为0，返回即可
        if (CollUtil.isEmpty(userPaidTrackList)) {
            for (Long needCheckBuyStateTrackId : needCheckBuyStateTrackIds) {
                map.put(needCheckBuyStateTrackId, 0);
            }
            return map;
        }
        //2.2 如果存在已购声音列表，将已购声音ID设置为1，未购买声音ID设置为0
        List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        for (Long needCheckBuyStateTrackId : needCheckBuyStateTrackIds) {
            //2.2.1 已购声音ID集合中包含待检查声音ID，说明该声音已购买 将购买声音ID设置为1
            //2.2.2 反之 将声音ID购买情况设置为0
            if (userPaidTrackIdList.contains(needCheckBuyStateTrackId)) {
                map.put(needCheckBuyStateTrackId, 1);
            } else {
                map.put(needCheckBuyStateTrackId, 0);
            }
        }
        return map;
    }

    /**
     * 返回指定用户指定专辑购买结果
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public boolean isPaidAlbum(Long userId, Long albumId) {
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getUserId, userId);
        queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        return count > 0;
    }

    /**
     * 查询用户指定专辑已购声音ID列表
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        queryWrapper.eq(UserPaidTrack::getUserId, userId);
        queryWrapper.select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        if (CollUtil.isNotEmpty(userPaidTrackList)) {
            return userPaidTrackList
                    .stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
        }
        return null;
    }

    @Autowired
    private StrategyFactory strategyFactory;

    /**
     * 虚拟物品发货,保存购买记录（声音、专辑、VIP会员）
     *
     * @param userPaidRecordVo
     * @return
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        //1.获取付款项目类型  付款项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = userPaidRecordVo.getItemType();

        //2.从策略工厂中获取付款项目类型对应的策略实现类对象
        PaidRecordStrategy strategy = strategyFactory.getStrategy(itemType);
        strategy.handlerPaidRecord(userPaidRecordVo);
    }

    /**
     * 更新VIP状态：处理过期会员
     *
     * @return
     */
    @Override
    public void updateVipExpireStatus(Date date) {
        //1.查询所有过期会员
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getIsVip, 1);
        queryWrapper.lt(UserInfo::getVipExpireTime, date);
        List<UserInfo> userInfoList = baseMapper.selectList(queryWrapper);
        //2.更新所有过期会员的VIP标识
        if (CollUtil.isNotEmpty(userInfoList)) {
            for (UserInfo userInfo : userInfoList) {
                userInfo.setIsVip(0);
                baseMapper.updateById(userInfo);
            }
        }
    }
}
